﻿//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
// Exo² - Tools - Studio/ExefReaderModule
// Copyright (C) 2007-2008 Samuel Lacroix and Raphaël Legouge
//
// Distributed under GNU General Public License
// See the License folder for details
// http://www.codeplex.com/exo2 (eng)
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using SlimDX;

namespace ExefReaderModule
{
    public partial class ExefReaderForm : BasicModule.MainForm
    {
        public ExefReaderForm()
        {
            InitializeComponent();

            ToolStripMenuItem fileMenu = new ToolStripMenuItem("File");
            this.MainMenuStrip.Items.Add(fileMenu);

            ToolStripMenuItem loadExef = new ToolStripMenuItem("Load Exef");
            fileMenu.DropDownItems.Add(loadExef);
            loadExef.Click += new EventHandler(loadExef_Click);

            ToolStripMenuItem unloadExef = new ToolStripMenuItem("Unload Exef");
            fileMenu.DropDownItems.Add(unloadExef);
            unloadExef.Click += new EventHandler(unloadExef_Click);

            ToolStripMenuItem displayMenu = new ToolStripMenuItem("Display");
            this.MainMenuStrip.Items.Add(displayMenu);

            ToolStripMenuItem topLevel = new ToolStripMenuItem("Highest level");
            displayMenu.DropDownItems.Add(topLevel);
            topLevel.Click += new EventHandler(topLevel_Click);

            ToolStripMenuItem levelUp = new ToolStripMenuItem("Level up");
            displayMenu.DropDownItems.Add(levelUp);
            levelUp.Click += new EventHandler(levelUp_Click);

            ToolStripMenuItem levelDown = new ToolStripMenuItem("Level down");
            displayMenu.DropDownItems.Add(levelDown);
            levelDown.Click += new EventHandler(levelDown_Click);

            ToolStripMenuItem leastLevel = new ToolStripMenuItem("Lowest level");
            displayMenu.DropDownItems.Add(leastLevel);
            leastLevel.Click += new EventHandler(leastLevel_Click);

            ToolStripSeparator Seperator0 = new ToolStripSeparator();
            displayMenu.DropDownItems.Add(Seperator0);

            ToolStripMenuItem changeCurrentPhysiology = new ToolStripMenuItem("Change current physiology...");
            displayMenu.DropDownItems.Add(changeCurrentPhysiology);
            changeCurrentPhysiology.Click += new EventHandler(changeCurrentPhysiology_Click);

            this.choosePhysiology = new PhysiologyChoice();

            ToolStripSeparator Seperator1 = new ToolStripSeparator();
            displayMenu.DropDownItems.Add(Seperator1);

            ToolStripMenuItem loadXFile = new ToolStripMenuItem("Load X file for the current physiology...");
            displayMenu.DropDownItems.Add(loadXFile);
            loadXFile.Click += new EventHandler(loadXFile_Click);

            ToolStripSeparator Seperator2 = new ToolStripSeparator();
            displayMenu.DropDownItems.Add(Seperator2);

            ToolStripMenuItem zoomIn = new ToolStripMenuItem("Zoom in");
            displayMenu.DropDownItems.Add(zoomIn);
            zoomIn.Click += new EventHandler(zoomIn_Click);

            ToolStripMenuItem zoomOut = new ToolStripMenuItem("Zoom out");
            displayMenu.DropDownItems.Add(zoomOut);
            zoomOut.Click += new EventHandler(zoomOut_Click);

            ToolStripSeparator Seperator3 = new ToolStripSeparator();
            displayMenu.DropDownItems.Add(Seperator3);

            ToolStripMenuItem resetView = new ToolStripMenuItem("Reset view");
            displayMenu.DropDownItems.Add(resetView);
            resetView.Click += new EventHandler(resetView_Click);
        }

        // Events
        private void loadExef_Click(object sender, EventArgs e) { LoadExef(); }
        private void unloadExef_Click(object sender, EventArgs e) { UnloadExef(); }
        private void ExefReaderForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            ResetDataStorage();
            ResetMenu();
        }
        private void ExefReaderForm_Load(object sender, EventArgs e) { InitializeDisplay(); }
        private void timer1_Tick(object sender, EventArgs e) { Run(); }
        private void leastLevel_Click(object sender, EventArgs e) { SetLastLevel(); }
        private void levelDown_Click(object sender, EventArgs e) { IncrementLevel(); }
        private void levelUp_Click(object sender, EventArgs e) { DecrementLevel(); }
        private void topLevel_Click(object sender, EventArgs e) { SetFirstLevel(); }
        private void resetView_Click(object sender, EventArgs e) { ResetView(); }
        private void zoomIn_Click(object sender, EventArgs e) { ZoomIn(); }
        private void zoomOut_Click(object sender, EventArgs e) { ZoomOut(); }
        private void changeCurrentPhysiology_Click(object sender, EventArgs e) { ChangeDisplayedPhysiology(); }
        private void loadXFile_Click(object sender, EventArgs e) { LoadXFile(); }


        //
        // Partie logique
        //
        // Les données chargées depuis l'exef sont stockées dans les structures suivantes
        // Nom de l'exef
        private string exefName;

        // Liste des physiologies
        private Dictionary<string, CPhysiology> physiologies;

        // Nom de la physiology par défaut
        private string defaultPhysiologyName;
        
        // Prépare la place pour les données chargée d'un exef
        private void ResetDataStorage()
        {
            this.exefName = "";
            this.physiologies = new Dictionary<string, CPhysiology>();
            this.defaultPhysiologyName = "";
        }

        // Chargement d'un fichier exef et stock les données
        private void LoadExef(string fileName)
        {
            ResetDataStorage();

            this.exefName = fileName;

            // Chargement des données depuis le fichier (appel de la dll C++)
            CDllWrapper.CreateReader();
            CDllWrapper.LoadExef(this.exefName);
            this.physiologies = CDllWrapper.GetPhysiologies();
            this.defaultPhysiologyName = CDllWrapper.GetDefaultPhysiologyName();
            CDllWrapper.DeleteReader();
        }

        //
        // Partie Menu
        //
        // Prépare le menu à la mise à jour
        private void ResetMenu()
        {
            // timer de rendu
            this.timer1.Enabled = false;

            // treeview
            this.treeView1.Nodes.Clear();
        }

        // Construction du treeview
        private void BuildTreeView()
        {
            // On créé l'arbre
            TreeNode root = new TreeNode(this.exefName);

            Dictionary<string, CPhysiology>.Enumerator pair = this.physiologies.GetEnumerator();
            Boolean End = pair.MoveNext();
            for (; End; )
            {
                string name = pair.Current.Key;
                CPhysiology physiology = pair.Current.Value;

                TreeNode newNode = new TreeNode(name);
                BuildPhysiologyNode(newNode, physiology);
                if (name == this.defaultPhysiologyName)
                {
                    newNode.NodeFont = new Font(System.Drawing.SystemFonts.DefaultFont, FontStyle.Underline);
                }
                if (name == this.physiologyDisplayed)
                {
                    newNode.BackColor = Color.Cyan;
                }
                root.Nodes.Add(newNode);

                End = pair.MoveNext();
            }

            root.Expand();
            this.treeView1.Nodes.Add(root);

        }

        // Construction d'un noeud correspondant à une physiology
        private void BuildPhysiologyNode(TreeNode node, CPhysiology physiology)
        {
            // Mecanique
            TreeNode mecanicNode = new TreeNode("Mecanics");

            // Masse
            TreeNode massNode = new TreeNode("Mass");
            massNode.Nodes.Add(new TreeNode(physiology.mass.ToString() + " kg"));

            // Densité
            TreeNode densityNode = new TreeNode("Density");
            densityNode.Nodes.Add(new TreeNode(physiology.density.ToString() + " kg/m^3"));

            // Volume
            TreeNode volumeNode = new TreeNode("Volume");
            volumeNode.Nodes.Add(new TreeNode(physiology.volume.ToString() + " m^3"));

            // Matrice d'inertie
            TreeNode inertiaMatrixNode = new TreeNode("Inertia matrix");
            inertiaMatrixNode.Nodes.Add(new TreeNode("x11 = " + physiology.inertiaMatrix11 + " kg * m^2"));
            inertiaMatrixNode.Nodes.Add(new TreeNode("x12 = " + physiology.inertiaMatrix12 + " kg * m^2"));
            inertiaMatrixNode.Nodes.Add(new TreeNode("x13 = " + physiology.inertiaMatrix13 + " kg * m^2"));
            inertiaMatrixNode.Nodes.Add(new TreeNode("x22 = " + physiology.inertiaMatrix22 + " kg * m^2"));
            inertiaMatrixNode.Nodes.Add(new TreeNode("x23 = " + physiology.inertiaMatrix23 + " kg * m^2"));
            inertiaMatrixNode.Nodes.Add(new TreeNode("x33 = " + physiology.inertiaMatrix33 + " kg * m^2"));

            mecanicNode.Nodes.Add(massNode);
            mecanicNode.Nodes.Add(densityNode);
            mecanicNode.Nodes.Add(volumeNode);
            mecanicNode.Nodes.Add(inertiaMatrixNode);

            // SphereTree
            TreeNode sphereTreeNode = new TreeNode("Sphere-Tree");
            BuildSphereTreeNode(sphereTreeNode, physiology.root, 0);

            node.Nodes.Add(mecanicNode);
            node.Nodes.Add(sphereTreeNode);
        }

        // Construction d'un noeud correspondant à un sphere tree
        private void BuildSphereTreeNode(TreeNode sphereTreeNode, CSphere sphere, int level)
        {
            TreeNode sphereNode = new TreeNode("Level " + level);

            // Infos sur la sphere
            // Centre
            TreeNode centerNode = new TreeNode("Center");
            centerNode.Nodes.Add(new TreeNode("x = " + sphere.center.X));
            centerNode.Nodes.Add(new TreeNode("y = " + sphere.center.Y));
            centerNode.Nodes.Add(new TreeNode("z = " + sphere.center.Z));
            sphereNode.Nodes.Add(centerNode);

            // Rayon
            TreeNode radiusNode = new TreeNode("Radius");
            radiusNode.Nodes.Add(new TreeNode(sphere.radius.ToString()));
            sphereNode.Nodes.Add(radiusNode);

            // Erreur
            TreeNode errorNode = new TreeNode("Error");
            errorNode.Nodes.Add(new TreeNode(sphere.error.ToString()));
            sphereNode.Nodes.Add(errorNode);

            // Enfants
            if (sphere.children.Count > 0)
            {
                TreeNode childrenNode = new TreeNode("Level " + (level + 1) + " (Children " + sphere.children.Count + ")");
                List<CSphere>.Enumerator iterator = sphere.children.GetEnumerator();
                Boolean End = iterator.MoveNext();
                for (; End; )
                {
                    BuildSphereTreeNode(childrenNode, iterator.Current, level + 1);
                    End = iterator.MoveNext();
                }
                sphereNode.Nodes.Add(childrenNode);
            }

            sphereTreeNode.Nodes.Add(sphereNode);
        }

        // Mise à jour du menu en fonction des données stockées
        private void UpdateMenu()
        {
            ResetMenu();

            // Reconstruction de l'arbre
            BuildTreeView();

            // Activation du rendu
            this.timer1.Enabled = true;

        }


///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

        // Fonction a appeler pour charger un exef
        public void LoadExef()
        {
            if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                // On charge l'exef
                LoadExef(this.openFileDialog1.FileName);

                // On lance la callback de fin de chargement de l'exef
                ExefLoaded();

                // On met à jour le menu
                UpdateMenu();
            }
        }

        // Fonction a appeler pour décharger un exef
        public void UnloadExef()
        {
            // On efface les données
            ResetDataStorage();

            // Initialise le menu avec les paramètres de démarrage
            ResetMenu();

            // On lance la callback de fin de déchargement
        }

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

        //
        // Affichage
        //
        // Données membre utile à l'initialisation de l'affichage
        private SlimDX.Direct3D9.PresentParameters presentParameters;
        private SlimDX.Direct3D9.Device device;

        private Boolean deviceLost;
        private double fov;
        private double ratio;
        float zoomFactor;
        private SlimDX.Direct3D9.Mesh sphereMesh;
        private SlimDX.Matrix cameraMatrix;
        private SlimDX.Matrix transformMatrix;
        private Boolean MeshWireFrame;
        private Boolean SphereTreeWireFrame;

        // Initialisation des matrices d'affichage
        private void ResetView()
        {
            this.cameraMatrix = SlimDX.Matrix.Translation(0.0f, 0.0f, 2.0f);
            this.transformMatrix = SlimDX.Matrix.Identity;
        }

        // Initialisation de l'affichage
        private void InitializeDisplay()
        { 
            // Initialisation de Direct3D
            SlimDX.Direct3D9.Direct3D direct3D = new SlimDX.Direct3D9.Direct3D();

            // Choix de l'adapteur de rendu
            SlimDX.Direct3D9.AdapterInformation adapter = direct3D.Adapters.DefaultAdapter;
            int adapterIndex = adapter.Adapter;

            // Choix du type de device
            SlimDX.Direct3D9.DeviceType deviceType = SlimDX.Direct3D9.DeviceType.Hardware;

            // Choix de la zone de rendu
            IntPtr handle = this.pictureBox1.Handle;

            // Choix des paramètres de création du device
            SlimDX.Direct3D9.CreateFlags createFlags = SlimDX.Direct3D9.CreateFlags.HardwareVertexProcessing;
            this.presentParameters = new SlimDX.Direct3D9.PresentParameters();

            // Choix des paramètres de rendu
            this.presentParameters.DeviceWindowHandle = handle;                                         // Rendu dans la picture box
            this.presentParameters.BackBufferHeight = this.pictureBox1.Height;                          // Taille de la zone de rendu
            this.presentParameters.BackBufferWidth = this.pictureBox1.Width;
            this.presentParameters.Windowed = true;                                                     // Mode fenètré
            this.presentParameters.BackBufferFormat = SlimDX.Direct3D9.Format.X8R8G8B8;                 // Format du backbuffer
            this.presentParameters.PresentFlags = SlimDX.Direct3D9.PresentFlags.DiscardDepthStencil;    // On vire le Depth et le Stencil buffer à chaque present

            this.device = new SlimDX.Direct3D9.Device(direct3D, adapterIndex, deviceType, handle, createFlags, presentParameters);

            // Création de la lumière 0
            SlimDX.Direct3D9.Light light0 = new SlimDX.Direct3D9.Light();
            light0.Type = SlimDX.Direct3D9.LightType.Directional;
            light0.Ambient = new SlimDX.Color4(new SlimDX.Vector4(0.25f, 0.25f, 0.25f, 1.0f));
            light0.Diffuse = new SlimDX.Color4(new SlimDX.Vector4(0.35f, 0.35f, 0.35f, 1.0f));
            light0.Direction = new SlimDX.Vector3(-1.0f, -1.0f, 1.0f);
            light0.Specular = new SlimDX.Color4(new SlimDX.Vector4(1.0f, 1.0f, 1.0f, 1.0f));

            // Enregistrement dans le device
            this.device.SetLight(0, light0);

            // Création de la lumière 1
            SlimDX.Direct3D9.Light light1 = new SlimDX.Direct3D9.Light();
            light1.Type = SlimDX.Direct3D9.LightType.Directional;
            light1.Ambient = new SlimDX.Color4(new SlimDX.Vector4(0.25f, 0.25f, 0.25f, 1.0f));
            light1.Diffuse = new SlimDX.Color4(new SlimDX.Vector4(0.35f, 0.35f, 0.35f, 1.0f));
            light1.Direction = new SlimDX.Vector3(1.0f, 0.0f, 1.0f);
            light1.Specular = new SlimDX.Color4(new SlimDX.Vector4(1.0f, 1.0f, 1.0f, 1.0f));

            // Enregistrement dans le device
            this.device.SetLight(1, light1);

            // Allumage des lumières
            this.device.EnableLight(0, true);
            this.device.EnableLight(1, true);

            // Création de la matrice de projection
            this.fov = (float)(Math.Sqrt(2.0) / 2.0);
            this.ratio = ((double)this.pictureBox1.Width) / this.pictureBox1.Height;
            float znear = 0.01f;
            float zfar = 10000.0f;
            SlimDX.Matrix projectionMatrix = SlimDX.Matrix.PerspectiveFovLH((float)this.fov, (float)this.ratio, znear, zfar);

            this.zoomFactor = 0.5f;

            // Enregistrement dans le device
            this.device.SetTransform(SlimDX.Direct3D9.TransformState.Projection, projectionMatrix);

            ResetView();

            // Création du mesh pour les spheres
            this.sphereMesh = SlimDX.Direct3D9.Mesh.CreateSphere(this.device, 1.0f, 16, 16);

            // Création du matériau pour les spheres
            this.sphereMaterial = new SlimDX.Direct3D9.Material();
            this.sphereMaterial.Ambient = new SlimDX.Color4(0.25f, 0.38f, 0.38f, 0.38f);
            this.sphereMaterial.Diffuse = new SlimDX.Color4(0.25f, 0.38f, 0.38f, 0.38f);
            this.sphereMaterial.Emissive = new SlimDX.Color4(0.0f, 0.0f, 0.0f, 0.0f);
            this.sphereMaterial.Power = 5.0f;
            this.sphereMaterial.Specular = new SlimDX.Color4(0.6f, 0.9f, 0.9f, 0.9f);

            // Renseignement sur l'état du device
            this.deviceLost = false;

            // Options d'affichage
            this.MeshWireFrame = false;
            this.SphereTreeWireFrame = true;
        }

        // Rendu 
        private void RenderScene()
        {
            // Début du rendu
            this.device.BeginScene();

            // Option d'affichage
            if (!this.MeshWireFrame)
                this.device.SetRenderState<SlimDX.Direct3D9.FillMode>(SlimDX.Direct3D9.RenderState.FillMode, SlimDX.Direct3D9.FillMode.Solid);
            else
                this.device.SetRenderState<SlimDX.Direct3D9.FillMode>(SlimDX.Direct3D9.RenderState.FillMode, SlimDX.Direct3D9.FillMode.Wireframe);

            this.device.SetTransform(SlimDX.Direct3D9.TransformState.View, this.cameraMatrix);

            this.device.SetTransform(SlimDX.Direct3D9.TransformState.World, this.transformMatrix);


            if (this.associatedMeshes[this.physiologyDisplayed] != null)
            {
                for (int i = 0; i < this.associatedMeshes[this.physiologyDisplayed].GetMaterials().Length; ++i)
                {
                    this.device.Material = this.associatedMeshes[this.physiologyDisplayed].GetMaterials()[i].MaterialD3D;
                    this.associatedMeshes[this.physiologyDisplayed].DrawSubset(i);
                }
            }



            // Option d'affichage
            if (!this.SphereTreeWireFrame)
                this.device.SetRenderState<SlimDX.Direct3D9.FillMode>(SlimDX.Direct3D9.RenderState.FillMode, SlimDX.Direct3D9.FillMode.Solid);
            else
                this.device.SetRenderState<SlimDX.Direct3D9.FillMode>(SlimDX.Direct3D9.RenderState.FillMode, SlimDX.Direct3D9.FillMode.Wireframe);


            List<CSphere>.Enumerator it = this.physiologiesSphereTreeSorted[this.physiologyDisplayed][this.levelDisplayed].GetEnumerator();
            Boolean End = !it.MoveNext();

            // Définition du matériau
            this.device.Material = this.sphereMaterial;

            // Pour chaque sphere du niveau
            while (!End)
            {
                // Définition de la position de la sphere
                SlimDX.Matrix T = SlimDX.Matrix.Multiply(SlimDX.Matrix.Translation(it.Current.center), this.transformMatrix);
                SlimDX.Matrix S = SlimDX.Matrix.Scaling((float)it.Current.radius, (float)it.Current.radius, (float)it.Current.radius);
                this.device.SetTransform(SlimDX.Direct3D9.TransformState.World, SlimDX.Matrix.Multiply(S, T));

                // On dessine la sphere
                this.sphereMesh.DrawSubset(0);

                // On décalle
                End = !it.MoveNext();
            }

            // fin du rendu
            this.device.EndScene();
        }

        // Mise à jour de la scène
        private void UpdateScene()
        {
            
        }

        // Nettoyage de la surface de rendu
        private void Clear()
        {
            if (this.device.Clear(SlimDX.Direct3D9.ClearFlags.Target | SlimDX.Direct3D9.ClearFlags.ZBuffer, new SlimDX.Color4(new SlimDX.Vector4(0.0f, 0.0f, 0.0f, 0.0f)), 1.0f, 0) == SlimDX.Direct3D9.ResultCode.DeviceLost)
                this.deviceLost = true;
        }

        // Affichage dans la surface de rendu
        private void Present()
        {

            if (this.device.Present() == SlimDX.Direct3D9.ResultCode.DeviceLost)
                this.deviceLost = true;
            
        }

        // Fonction a appeler pour effectuer le rendu d'un frame
        public void Run()
        {
            if (this.deviceLost)
            {
                SlimDX.Result Hr = this.device.TestCooperativeLevel();

                if (Hr.Code == SlimDX.Direct3D9.ResultCode.DeviceLost.Code)
                {
                    return;
                }
                else
                {
                    if (Hr.Code == SlimDX.Direct3D9.ResultCode.DeviceNotReset.Code)
                    {
                        this.presentParameters.BackBufferWidth = this.pictureBox1.Width;
                        this.presentParameters.BackBufferHeight = this.pictureBox1.Height;

                        this.device.Reset(this.presentParameters);

                        return;
                    }
                    else
                    {
                        return;
                    }
                }
            }
            else
            {
                UpdateScene();
                Clear();
                RenderScene();
                Present();
            }
        }

        // Modification des options d'affichage
        private void SetMeshWireFrame(Boolean ON_OFF)
        {
            this.MeshWireFrame = ON_OFF;
        }
        private void SetSphereTreeWireFrame(Boolean ON_OFF)
        {
            this.SphereTreeWireFrame = ON_OFF;
        }
        private void IncrementLevel()
        {
            this.levelDisplayed = Math.Min(this.levelDisplayed + 1, this.physiologiesSphereTreeSorted[this.physiologyDisplayed].Count - 1);
        }
        private void DecrementLevel()
        {
            this.levelDisplayed = Math.Max(0, this.levelDisplayed - 1);
        }
        private void SetFirstLevel()
        {
            this.levelDisplayed = 0;
        }
        private void SetLastLevel()
        {
            this.levelDisplayed = this.physiologiesSphereTreeSorted[this.physiologyDisplayed].Count - 1;
        }
        private void SetDisplayedPhysiology(string physiologyName)
        {
            this.physiologyDisplayed = physiologyName;
            ResetMenu();
            BuildTreeView();
            this.timer1.Enabled = true;
            this.levelDisplayed = Math.Min(this.levelDisplayed, this.physiologiesSphereTreeSorted[this.physiologyDisplayed].Count - 1);
        }
        private void ZoomIn()
        {
            this.cameraMatrix = SlimDX.Matrix.Multiply(this.cameraMatrix, SlimDX.Matrix.Translation(0.0f, 0.0f, -this.cameraMatrix.M43 * this.zoomFactor));
        }
        private void ZoomOut()
        {
            this.cameraMatrix = SlimDX.Matrix.Multiply(this.cameraMatrix, SlimDX.Matrix.Translation(0.0f, 0.0f, this.cameraMatrix.M43 * this.zoomFactor));
        }

        private PhysiologyChoice choosePhysiology;


///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

        // Matériau pour afficher les spheres
        private SlimDX.Direct3D9.Material sphereMaterial;

        // Spheres triées par niveau
        private Dictionary<string, List<List<CSphere>>> physiologiesSphereTreeSorted;

        // Meshs à afficher en même temps
        private Dictionary<string, SlimDX.Direct3D9.Mesh> associatedMeshes;

        // Niveau affiché
        private int levelDisplayed;

        // Nom de la physiology courante
        private string physiologyDisplayed;

        private Dictionary<string, List<List<CSphere>>> LoadSphereTreeByLevel()
        {
            Dictionary<string, List<List<CSphere>>> result = new Dictionary<string, List<List<CSphere>>>();

            Boolean End = false;
            Dictionary<string, CPhysiology>.Enumerator it = this.physiologies.GetEnumerator();
            End = !it.MoveNext();

            while (!End)
            {
                result.Add(it.Current.Value.name, LoadPhysiologySphereByLevel(it.Current.Value));
                End = !it.MoveNext();
            }

            return result;
        }

        private List<List<CSphere>> LoadPhysiologySphereByLevel(CPhysiology physio)
        {
            List<List<CSphere>> result = new List<List<CSphere>>();

            List<CSphere> list = new List<CSphere>();
            list.Add(physio.root);
            result.Add(list);

            if (physio.root.children.Count > 0)
            {
                list = new List<CSphere>();
                list.AddRange(physio.root.children);
                result.Add(list);

                List<CSphere> noChild = new List<CSphere>();

                for (int i = 0; i < physio.root.children.Count; ++i)
                    LoadSubLevel(result, 2, physio.root.children[i], noChild);
            }

            return result;
        }

        private void LoadSubLevel(List<List<CSphere>> result, int level, CSphere motherSphere, List<CSphere> noChild)
        {
            if (motherSphere.children.Count < 1)
                return;

            if (result.Count <= level)
                result.Add(new List<CSphere>());

            result[level].AddRange(motherSphere.children);

            for (int i = 0; i < motherSphere.children.Count; ++i)
            {
                LoadSubLevel(result, level + 1, motherSphere.children[i], noChild);
            }
        }

        // Fonction lancée quand l'exef est fini de charger
        private void ExefLoaded()
        {
            // Trie des spheres des physiologies
            this.physiologiesSphereTreeSorted = LoadSphereTreeByLevel();

            // Initialisation des variables d'affichage
            this.levelDisplayed = 0;
            this.physiologyDisplayed = this.defaultPhysiologyName;

            // Création du dictionnaire pour les mesh
            this.associatedMeshes = new Dictionary<string, SlimDX.Direct3D9.Mesh>();
            Dictionary<string, List<List<CSphere>>>.Enumerator it = this.physiologiesSphereTreeSorted.GetEnumerator();
            Boolean End = it.MoveNext();

            // On copie les noms des physiologies
            while (End)
            {
                this.associatedMeshes.Add(it.Current.Key, null);
                End = it.MoveNext();
            }
        }

        // Fonction lancée quand l'exef est fini de décharger
        private void ExefUnloaded()
        {
            
        }

        private void LoadXFile()
        { 
            if (this.openFileDialog2.ShowDialog() == DialogResult.OK)
            {
                // On décharge l'ancien s'il y en a un
                UnloadXFile();

                // On charge le nouveau
                string fileName = this.openFileDialog2.FileName;
                LoadXFileInPhysiology(fileName, this.physiologyDisplayed);


            }
        }

        private void ChangeDisplayedPhysiology()
        {
            this.choosePhysiology.ResetForm();
            string[] names = new string[this.physiologies.Keys.Count];
            this.physiologies.Keys.CopyTo(names, 0);
            this.choosePhysiology.SetNames(names);

            if (this.choosePhysiology.ShowDialog() == DialogResult.OK)
            {
                SetDisplayedPhysiology(this.choosePhysiology.GetSelectedName());
            }
        }

        private void LoadXFileInPhysiology(string fileName, string physiologyName)
        {
            this.associatedMeshes[physiologyName] = SlimDX.Direct3D9.Mesh.FromFile(this.device, fileName, SlimDX.Direct3D9.MeshFlags.DoNotClip);
        }


        private void UnloadXFile()
        {

        }

        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            IncrementLevel();
        }

        private void decrementToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DecrementLevel();
        }

        private void topToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetFirstLevel();
        }

        private void bottomToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetLastLevel();
        }

        private void chargerXPourLaPhysiologyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LoadXFile();
        }

        private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                this.contextMenuStrip2.Show((Control)sender, e.Location);
            }
        }

        private void zoomInToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ZoomIn();
        }

        private void zoomOutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ZoomOut();
        }

        private void showHighestLevelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetFirstLevel();
        }

        private void showLevelUpToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DecrementLevel();
        }

        private void showLevelDownToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IncrementLevel();
        }

        private void showToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetLastLevel();
        }

        private void changeDisplayedPhysiologyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ChangeDisplayedPhysiology();
        }

        private void loadXFileToDisplayWithThisPhysiologyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LoadXFile();
        }

        private void resetViewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ResetView();
        }

        private void turnXAxisToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.transformMatrix = SlimDX.Matrix.Multiply(this.transformMatrix, SlimDX.Matrix.RotationX((float)(Math.PI / 16)));
        }

        private void turnXAxisToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            this.transformMatrix = SlimDX.Matrix.Multiply(this.transformMatrix, SlimDX.Matrix.RotationX((float)(-Math.PI / 16)));
        }

        private void turnYAxisToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.transformMatrix = SlimDX.Matrix.Multiply(this.transformMatrix, SlimDX.Matrix.RotationY((float)(Math.PI / 16)));
        }

        private void turnYAxisToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            this.transformMatrix = SlimDX.Matrix.Multiply(this.transformMatrix, SlimDX.Matrix.RotationY((float)(-Math.PI / 16)));
        }

        private void turnZAxisToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.transformMatrix = SlimDX.Matrix.Multiply(this.transformMatrix, SlimDX.Matrix.RotationZ((float)(Math.PI / 16)));
        }

        private void turnZAxisToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            this.transformMatrix = SlimDX.Matrix.Multiply(this.transformMatrix, SlimDX.Matrix.RotationZ((float)(-Math.PI / 16)));
        }

    }

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////


    [StructLayout(LayoutKind.Sequential)]
    public class CPhysiology
    {
        public string name;

        public double mass;
        public double volume;
        public double density;

        public double inertiaMatrix11;
        public double inertiaMatrix12;
        public double inertiaMatrix13;
        public double inertiaMatrix22;
        public double inertiaMatrix23;
        public double inertiaMatrix33;

        public CSphere root;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class CSphere
    {
        public List<CSphere> children;

        public SlimDX.Vector3 center;
        public double radius;
        public double error;
    }

    public class CDllWrapper
    {
        ///////////////////////////////////////////////////////////////////
        // Partie programme C#
        public static void CreateReader()
        {
            pReader = NewReader();
        }
        public static void LoadExef(string fileName)
        {
            IntPtr pFileName = Marshal.StringToHGlobalUni(fileName);
            LoadExef(pReader, pFileName);
        }

        public static void DeleteReader()
        {
            DeleteReader(pReader);
        }

        public static string GetDefaultPhysiologyName()
        {
            return Marshal.PtrToStringUni(GetDefaultPhysiologyName(pReader));
        }

        public static Dictionary<string, CPhysiology> GetPhysiologies()
        {
            Dictionary<string, CPhysiology> physiologies = new Dictionary<string, CPhysiology>();

            IntPtr pPhysio = GetCurrentPhysiology(pReader);
            while (pPhysio.ToInt32() != 0)
            {
                CPhysiology physiology = CreatePhysiology(pPhysio);
                physiologies.Add(physiology.name, physiology);

                MoveToNextPhysiology(pReader);
                pPhysio = GetCurrentPhysiology(pReader);
            }

            return physiologies;
        }

        private static CPhysiology CreatePhysiology(IntPtr pPhysiology)
        {
            CPhysiology physio = new CPhysiology();

            physio.density = GetPhysiologyDensity(pPhysiology);
            physio.inertiaMatrix11 = GetPhysiologyInertiaMatrix11(pPhysiology);
            physio.inertiaMatrix12 = GetPhysiologyInertiaMatrix12(pPhysiology);
            physio.inertiaMatrix13 = GetPhysiologyInertiaMatrix13(pPhysiology);
            physio.inertiaMatrix22 = GetPhysiologyInertiaMatrix22(pPhysiology);
            physio.inertiaMatrix23 = GetPhysiologyInertiaMatrix23(pPhysiology);
            physio.inertiaMatrix33 = GetPhysiologyInertiaMatrix33(pPhysiology);
            physio.mass = GetPhysiologyMass(pPhysiology);
            physio.name = Marshal.PtrToStringUni(GetPhysiologyName(pPhysiology));
            physio.root = CreateSphere(GetPhysiologyRoot(pPhysiology));
            physio.volume = GetPhysiologyVolume(pPhysiology);

            return physio;
        }

        private static CSphere CreateSphere(IntPtr pSphere)
        {
                CSphere sphere = new CSphere();

                sphere.center.X = (float)GetSphereCenterX(pSphere);
                sphere.center.Y = (float)GetSphereCenterY(pSphere);
                sphere.center.Z = (float)GetSphereCenterZ(pSphere);
                sphere.error = GetSphereError(pSphere);
                sphere.radius = GetSphereRadius(pSphere);
                sphere.children = new List<CSphere>();

                SetFirstChildren(pSphere);
                
                IntPtr pChild = GetCurrentChildren(pSphere);
                while (pChild.ToInt32() != 0)
                {
                CSphere child = CreateSphere(pChild);
                sphere.children.Add(child);

                MoveToNextChildren(pSphere);
                pChild = GetCurrentChildren(pSphere);
                }

                return sphere;
        }

        ///////////////////////////////////////////////////////////////////
        // Partie dll C++
        private static IntPtr pReader;
#if DEBUG
        private const string dllName = "exefreader_Debug.dll";
#else
        private const string dllName = "exefreader_Release.dll";
#endif

        // Fonctions de la DLL à wrapper
        [DllImport(dllName, EntryPoint = "NewReader")]
        private static extern IntPtr NewReader();

        [DllImport(dllName, EntryPoint = "DeleteReader")]
        private static extern void DeleteReader(IntPtr pReader);

        [DllImport(dllName, EntryPoint = "LoadExef")]
        private static extern void LoadExef(IntPtr pReader, IntPtr fileName);

        [DllImport(dllName, EntryPoint = "GetDefaultPhysiologyName")]
        private static extern IntPtr GetDefaultPhysiologyName(IntPtr pReader);

        [DllImport(dllName, EntryPoint = "GetPhysiology")]
        private static extern IntPtr GetCurrentPhysiology(IntPtr pReader);

        [DllImport(dllName, EntryPoint = "MoveToNextPhysiology")]
        private static extern void MoveToNextPhysiology(IntPtr pReader);

        [DllImport(dllName, EntryPoint = "GetPhysiologyDensity")]
        private static extern double GetPhysiologyDensity(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetPhysiologyInertiaMatrix11")]
        private static extern double GetPhysiologyInertiaMatrix11(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetPhysiologyInertiaMatrix12")]
        private static extern double GetPhysiologyInertiaMatrix12(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetPhysiologyInertiaMatrix13")]
        private static extern double GetPhysiologyInertiaMatrix13(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetPhysiologyInertiaMatrix22")]
        private static extern double GetPhysiologyInertiaMatrix22(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetPhysiologyInertiaMatrix23")]
        private static extern double GetPhysiologyInertiaMatrix23(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetPhysiologyInertiaMatrix33")]
        private static extern double GetPhysiologyInertiaMatrix33(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetPhysiologyMass")]
        private static extern double GetPhysiologyMass(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetPhysiologyName")]
        private static extern IntPtr GetPhysiologyName(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetPhysiologyRoot")]
        private static extern IntPtr GetPhysiologyRoot(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetPhysiologyVolume")]
        private static extern double GetPhysiologyVolume(IntPtr pPhysio);

        [DllImport(dllName, EntryPoint = "GetSphereCenterX")]
        private static extern double GetSphereCenterX(IntPtr pSphere);

        [DllImport(dllName, EntryPoint = "GetSphereCenterY")]
        private static extern double GetSphereCenterY(IntPtr pSphere);

        [DllImport(dllName, EntryPoint = "GetSphereCenterZ")]
        private static extern double GetSphereCenterZ(IntPtr pSphere);

        [DllImport(dllName, EntryPoint = "GetSphereError")]
        private static extern double GetSphereError(IntPtr pSphere);

        [DllImport(dllName, EntryPoint = "GetSphereRadius")]
        private static extern double GetSphereRadius(IntPtr pSphere);

        [DllImport(dllName, EntryPoint = "SetFirstChildren")]
        private static extern IntPtr SetFirstChildren(IntPtr pSphere);

        [DllImport(dllName, EntryPoint = "GetCurrentChildren")]
        private static extern IntPtr GetCurrentChildren(IntPtr pSphere);

        [DllImport(dllName, EntryPoint = "MoveToNextChildren")]
        private static extern void MoveToNextChildren(IntPtr pSphere);

    }
}
